#include"imp/imp_usart.h"
#include "log.h"
#include "hilog/log_cpp.h"
#include <stdio.h>

#include "serial/serial.h"
#include "system_ability_definition.h"
#include "imp/wang_debug.h"  
#include <mutex>
#include <queue>
#include <thread>
namespace OHOS{
namespace Usart_Standard{
        

    
using HiviewDFX::HiLog;

// namespace {
// constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, 0xD001800, "Usart_Ability"};
// }

REGISTER_SYSTEM_ABILITY_BY_ID(UsartAbility, USART_SERVICE_ID, true);

std::recursive_mutex PORTS_MUTEX;
struct loop_reader{
    UsartAbility::port_handle port_hd=nullptr;
    std::vector<uint8_t> queue_;
    bool volatile keep_spinning;
    std::recursive_mutex* p_mutex=nullptr;
    loop_reader():p_mutex( new std::recursive_mutex() ){;}
    ~loop_reader(){delete p_mutex;keep_spinning=false;}
    void keep_getting_one_char_and_push(){
        uint8_t c[1];
        while(keep_spinning){
            port_hd->read(c,1);
            auto lg=std::lock_guard(*p_mutex);
            queue_.push_back(c[0]);
        }
    }
    void spin(){
        keep_spinning=true;
        std::thread th=std::thread([](loop_reader* lr){lr->keep_getting_one_char_and_push();},this);
        th.detach();
    }
    void end_spinning(){keep_spinning=false;}
    std::string get(){
        std::vector<uint8_t> temp;
        {
            auto lg=std::lock_guard(*p_mutex);
            std::swap(queue_,temp);
        }
        std::string s(temp.begin(),temp.end());
        return s;
    }
};

std::map<std::string,UsartAbility::port_handle> ports;


std::map<std::string,loop_reader> loop_reader_from_port;

UsartAbility::UsartAbility(int32_t saId, bool runOnCreate) : SystemAbility(saId, runOnCreate){
    HiLog::Info(LABEL, ":%s called", __func__);
    HiLog::Info(LABEL, "UsartAbility()");
}

UsartAbility::~UsartAbility(){
    HiLog::Info(LABEL, ":%{public}s called", __func__);
    HiLog::Info(LABEL, "~UsartAbility()");
}

void UsartAbility::OnDump() { }
//void UsartAbility::OnDebug() { }
//void UsartAbility::OnTest() { }
void UsartAbility::OnAddSystemAbility(int32_t systemAbilityId, const std::string& deviceId){}
void UsartAbility::OnStart(){
    HiLog::Info(LABEL, "UsartAbility::OnStart()");
    HiLog::Info(LABEL, "UsartAbility:%s called:-----Publish------", __func__);
    bool res = Publish(this);
    if (res) {
        HiLog::Error(LABEL, "UsartAbility::%s res == false",__func__);
    }
    HiLog::Info(LABEL, "UsartAbility::%s called:AddAbilityListener_OS_TST----beg-----", __func__);
    AddSystemAbilityListener(DISTRIBUTED_SCHED_TEST_OS_ID);
    HiLog::Info(LABEL, "UsartAbility::%s called:AddAbilityListener_OS_TST----end-----", __func__);

    HiLog::Info(LABEL, "UsartAbility::%s called:StopAbility_OS_TST----beg-----", __func__);
    StopAbility(DISTRIBUTED_SCHED_TEST_OS_ID);
    HiLog::Info(LABEL, "UsartAbility::%s called:StopAbility_OS_TST----end-----", __func__);
    return;
}

void UsartAbility::OnStop(){ 
    HiLog::Info(LABEL, "UsartAbility::OnStop");
}

std::optional<UsartAbility::port_handle> UsartAbility::get_handle_and_assert_open(const std::string & name){
    if(auto it=ports.find(name);it!=ports.end()){
        if(it->second->isOpen()){
            return it->second;
        }
        else{
            HiLog::Error(LABEL, "UsartAbility::%s !! PORT ISN'T OPEN !!!", __func__);
            return std::nullopt;
        }
    }
    else{
        HiLog::Error(LABEL, "UsartAbility::%s !! PORT DOESN'T EXIST !!!", __func__);
        return std::nullopt;
    }
}


int UsartAbility::open(const std::string& name) {
    auto _g = std::lock_guard(PORTS_MUTEX);
    HiLog::Debug(LABEL,"UsartAbility::open %{public}s\n",name.c_str());
    if(is_open(name)){return 0;}
    port_handle sp=new serial::Serial(name,115200,serial::Timeout::simpleTimeout(1000*1000));
    HiLog::Debug(LABEL,"UsartAbility::open -- sp= %{public}lld\n",(long long)sp);
    ports.emplace(name,sp);
    HiLog::Debug(LABEL,"UsartAbility::open >> sp->open\n");
    sp->open();
    HiLog::Debug(LABEL,"UsartAbility::open -- tseting whether open is complete\n");
    bool res = sp->isOpen();
    HiLog::Debug(LABEL,"UsartAbility::open << %{public}s\n",(res?"complete":"incomplete !!!") );
    return res? 0 :-1;
}
int UsartAbility::close(const std::string& name) {
    auto _g = std::lock_guard(PORTS_MUTEX);
    if(!is_open(name)){return 0;}
    port_handle sp=ports[name];
    sp->close();
    ports.erase(name);
    delete sp;
    return 0;
}
bool UsartAbility::is_open(const std::string& name) {
    auto _g = std::lock_guard(PORTS_MUTEX);
    WANG_DEBUG("UsartAbility::is_open\n");
    if(ports.find(name)==ports.end()){return false;}
    WANG_DEBUG("UsartAbility::is_open -- ports[name]= %lld\n",(long long)ports[name]);
    WANG_DEBUG("UsartAbility::is_open >> sp->is_open\n");
    const bool res= ports[name]->isOpen();
    WANG_DEBUG("UsartAbility::is_open << %s\n",(res?"true":"false"));
    return res;
}

int UsartAbility::write(const std::string& name, const std::string& data) {
    auto _g = std::lock_guard(PORTS_MUTEX);
    auto osp=get_handle_and_assert_open(name);
    auto sp=osp.value();
    sp->read(1);
    int res= sp->write(data);
    sp->flushOutput();
    return res;
}



std::string UsartAbility::read(const std::string& name, size_t bytes) {
    auto _g = std::lock_guard(PORTS_MUTEX);
    auto osp=get_handle_and_assert_open(name);
    if( ! osp.has_value()){return "";}

    auto sp=osp.value();

    std::string res=sp->read(bytes);
    return res;
}
std::string UsartAbility::readline(const std::string& name) {
    auto _g = std::lock_guard(PORTS_MUTEX);
    auto osp=get_handle_and_assert_open(name);
    if( ! osp.has_value()){return "";}
    
    auto sp=osp.value();

    std::string res=sp->readline();
    return res;
}


void UsartAbility::begin_read_loop(const std::string& name){
    auto _g = std::lock_guard(PORTS_MUTEX);
    auto status =loop_reader_from_port.emplace(name,loop_reader());
    loop_reader & looper = status.first->second;
    looper.port_hd=ports[name];
    looper.spin();
}

void UsartAbility::end_read_loop(const std::string& name){
    auto _g = std::lock_guard(PORTS_MUTEX);
    loop_reader& looper=loop_reader_from_port[name];
    looper.end_spinning();
}

std::string UsartAbility::read_from_looper(const std::string& name){
    auto _g = std::lock_guard(PORTS_MUTEX);
    loop_reader& looper=loop_reader_from_port[name];
    return looper.get();
}



}//namespace Usart_Standard
}//namespace OHOS